﻿(function(window){
var 
	mooluLazy = function(){
		return new muLazy(Array.prototype
					.slice.apply(arguments));
	},

	muLazy = function(arryConfig){
		this._init(arryConfig);
		
		if(this.isFinish()) return;
		
		this._initMode();
		this.resize(true);
	};
		
muLazy.prototype = {
		
	_init : function(params){
		var 
			cfg = {},
			len = params.length,
			p, param;
		
		for(var i=1; i<len; i++){
		    
			param = params[i];
			switch(typeof param){
			    
				case 'number':
				
					break;
				case 'string':
				
					break;
				case 'function':
					
					break;
				case 'object':
					
					for(p in param){
					    
						cfg[p] = param[p];
					}
			}
		}
		// the remaining elements which 
		// is waiting for loading
		this._elems = params[0];
		this._rect = {};
		this._range = {};
		this._loadData = null;
		this._timer = null;
		this._lock = false;
		
		this._index = 0;
		this._direction = 0;
		this._lastScroll = {'left': 0, 'top': 0};
		this._setElems = function(){};
		
		var opt = this._setOptions(params[1]);
		
		this.delay = opt.delay;
		this.threshold = opt.threshold;
		this.beforeLoad = opt.beforeLoad;
		
		this._onLoadData = opt.onLoadData;
		this._container = 
			this._initContainer($$(this.options.container));
		
		return this;
	},
	
	_setOptions : function(options){
		this.options = {
			container: window,
			mode: 'dynamic',
			threshold: 0,
			delay: 100,
			beforeLoad: function(){},
			onLoadData: function(){}
		};
		return $$OBEA.extend(this.options, options || {});
	},
	
	_initMode : function(){
		switch(this.options.mode.toLowerCase()){
			case 'vertical':
				this._initStatic('vertical', 'vertical');
				break;
			case 'horizontal':
				this._initStatic('horizontal','horizontal');
				break;
			case 'cross':
			case 'cross-vertical':
				this._initStatic('cross', 'vertical');
				break;
			case 'cross-horizontal':
				this._initStatic('cross', 'horizontal');
				break;
			case 'dynamic':
			default:
				this._loadData = this._loadDynamic;
		}
	},
	
	_initStatic : function(mode, direction){
		var isVertical = direction == 'vertical';
		if(mode == 'cross'){
			this._crossDirection = $$OBEA.bind(this._getCrossDirection, this,
				isVertical ? '_verticalDirection' : '_horizontalDirection',
				isVertical ? '_horizontalDirection' : '_verticalDirection');
		}
		var 
			pos = isVertical ? 'top' : 'left',
			sortFunction = function(x, y){
				return x._rect[pos] - y._rect[pos];
			},
			getRect = function(elem){
				elem._rect = this._getRect(elem);
				return elem;
			};
		this._setElems = function(){
			this._elems = $$OBEA.map(this._elems, getRect, this)
					.sort(sortFunction);
		}
		
		this._loadData = $$OBEA.bind(this._loadStatic, this,
			'_' + mode + 'Direction',
			$$OBEA.bind(this._outofRange, this, mode, '_' + direction + 'BeforeRange'),
			$$OBEA.bind(this._outofRange, this, mode, '_' + direction + 'AfterRange')
		);
	},
	
	_initContainer : function(container){
		var doc = document,
			isWindow = 
				container == window || 
				container == doc || 
				!container.tagName ||
				(/^(?:body|html)$/i).test(container.tagName);	   
		
		if(isWindow){
			container = doc.compatMode == 'CSS1Compat' ?
				doc.documentElement : doc.body;
		}
		
		var oThis = this,
			width = 0,
			height = 0;
		
		this.load = $$OBEA.bind(this._load, this);
		this.resize = $$OBEA.bind(this._resize, this);
		this.delayLoad = function(){
			oThis._delay(oThis.load);
		};
		this.delayResize = function(){
			var clientWidth = container.clientWidth,
				clientHeight = container.clientHeight;
			
			if(clientWidth != width || clientHeight != height){
				width = clientWidth;
				height = clientHeight;
				oThis._delay(oThis.resize);
			}
		};
		
		this._binder = isWindow ? window : container;
		
		$$OBEA.helpe.addEvent(this._binder, 'scroll', this.delayLoad);
		isWindow && $$OBEA.helpe.addEvent(this._binder, 'resize', this.delayResize);

		this._getContainerRect = isWindow && ('innerHeight' in window)
			? function(){ return {
				'left' : 0,
				'right' : window.innerWidth,
				'top' : 0,
				'bottom' : window.innerHeight				
			}}
			: function(){
				return oThis._getRect(container);
			};
		
		this._getScroll = isWindow
			? function(){ return {
				'left' : $$OBEA.getScrollLeft(),
				'top' : $$OBEA.getScrollTop()
			}}
			: function(){ return{
				'left' : container.scrollLeft,
				'top' : container.scrollTop
			}}
		
		return container;
	},
	
	_delay : function(run){
		clearTimeout(this._timer);
		if(this.isFinish()) return;
		var oThis = this,
			delay = this.delay;
			
		if(this._lock){
			this._timer = setTimeout(function(){
				oThis._delay(run);
			}, delay)
		}else{
			this._lock = true;
			run();
			setTimeout(function(){
				oThis._lock = false;
			}, delay);
		}
	},
	
	_resize : function(change){
		if(this.isFinish()) return;
		this._rect = this._getContainerRect();
		if(change){
			this._setElems();
		}
		this._load(true);
	},
	
	_load : function(force){
		if(this.isFinish()) return;
		var 
			rect = this._rect,
			scroll = this._getScroll(),
			left = scroll.left,
			top = scroll.top,
			threshold = Math.max(0, this.threshold | 0);
		
		this._range = {
			top: rect.top + top - threshold,
			bottom: rect.bottom + top + threshold,
			left: rect.left + left - threshold,
			right: rect.right + left + threshold
		}
		
		this.beforeLoad();
		this._loadData(force);
	},
	
	_loadDynamic : function(){
		this._elems	= $$OBEA.filter(this._elems, function(elem){
				return !this._insideRange(elem);
		}, this);
	},
	
	_loadStatic : function(direction, beforeRange, afterRange, force){
		direction = this[direction](force);
		if(!direction) return;
		
		var elems = this._elems,
			i = this._index,
			begin = [],
			middle = [],
			end = [];
		
		if(direction > 0){
			begin = elems.slice(0, i);
			for(var len = elems.length; i<len; i++){
				if(afterRange(middle, elems[i])){
					end = elems.slice(i + 1);
					break;
				}
			}
			i = begin.length + middle.length - 1;
		}else{
			end = elems.slice(i + 1);
			for(; i>=0; i--){
				if(beforeRange(middle, elems[i])){
					begin = elems.slice(0, i);
					break;
				}
			}
			middle.reverse();
		}
		this._index = Math.max(0, i);
		this._elems = begin.concat(middle, end);
	},
	
	_verticalDirection : function(force){
		return this._getDirection(force, 'top');
	},
	
	_horizontalDirection : function(force){
		return this._getDirection(force, 'left');
	},
	
	_getDirection : function(force, scroll){
		var 
			now = this._getScroll()[scroll],
			_scroll = this._lastScroll;
		
		if(force){
			_scroll[scroll] = now;
			this._index = 0;
			return 1;
		}
		
		var old = _scroll[scroll];
		_scroll[scroll] = now;
		
		return now - old;
	},
	
	_getCrossDirection : function(primary, secondary, force){
		
		var direction;
		if(!force){
			direction = this[primary]();
			secondary = this[secondary]();
			
			if(!direction && !secondary){
				return 0;
			}else if(!direction){
				if(this._direction){
					direction = -this._direction;
				}else{
					force = true;
				}
			}else if(secondary && direction * this._direction >= 0){
				force = true;
			}
		}
		
		if(force){
			this._lastScroll = this._getScroll();
			this._index = 0;
			direction = 1;
		}
		
		return (this._direction = direction);
	},
	
	_getRect : function(node){
		var 
			n = node,
			left = 0,
			top = 0;
		
		while(n){
			left += n.offsetLeft;
			top += n.offsetTop;
			n = n.offsetParent;
		}
		
		return {
			'left': left,
			'right': left + node.offsetWidth,
			'top': top,
			'bottom': top + node.offsetHeight			
		};
	},
	
	_insideRange : function(elem, mode){
		var 
			range = this._range,
			rect = elem._rect || this._getRect(elem),
			insideH = rect.right >= range.left && rect.left <= range.right,
			insideV = rect.bottom >= range.top && rect.top <= range.bottom,
			inside = {
				'horizontal': insideH,
				'vertical': insideV,
				'cross': insideH && insideV
			}[mode || 'cross'];
		
		if(inside){
			this._onLoadData(elem);
		}
		return inside;
	},
	
	_outofRange : function(mode, compare, middle, elem){
		if(!this._insideRange(elem, mode)){
			middle.push(elem);
			return this[compare](elem._rect);
		}
	},
	
	_horizontalBeforeRange: function(rect){
		return rect.right < this._range.left;
	},
	
	_horizontalAfterRange: function(rect){
		return rect.left > this._range.right;
	},
	
	_verticalBeforeRange: function(rect){
		return rect.bottom < this._range.top;
	},
	
	_verticalAfterRange: function(rect){
		return rect.top > this._range.bottom;
	},
	
	isFinish : function(){
		if(!this._elems || !this._elems.length){
			this.destroy();
			return true;
		}else{
			return false;
		}
	},
	
	destroy : function(load){
		clearTimeout(this._timer);
		if(this._elems || this._binder){
			if(load && this._elems){
				$$OBEA.forEach(this.elems, this._onLoadData, this)
			}
			
			$$OBEA.helpe.removeEvent(this._binder, 'scroll', this.delayLoad);
			$$OBEA.helpe.removeEvent(this._binder, 'resize', this.delayResize);
			
			this._elems = this._binder = null;
		}
	}
		
	
};

window.muLazy = muLazy;
window.mooluLazy = mooluLazy;

})(window)